using System;
using System.Collections;
using System.Diagnostics;
using System.IO;

using NUnit.Framework;

using BuildAutoIncrement;

namespace TestUnits {

    [TestFixture]
    public class VS7SolutionTest {

        public VS7SolutionTest() {
            m_solutionSource = "..\\..\\Runtimes\\VS7.1TestSolution1\\TestSolution.sln";
            m_solutionDestination = Path.GetTempPath();
            SolutionFilename = Path.Combine(m_solutionDestination, Path.GetFileName(m_solutionSource));
            m_solutionTestHelper = new SolutionTestHelper(m_solutionSource);

            string projectDestination = Path.Combine(m_solutionDestination, Project1);
            Project project = new CSharpProject(projectDestination);
            project.AddFile("AssemblyInfo.cs");
            project.AddFile("Class1.cs");
            m_solutionTestHelper.AddProject(Project1, project);

            projectDestination = Path.Combine(m_solutionDestination, Project2);
            project = new CSharpProject(projectDestination);
            project.AddFile("AssemblyInfo.cs");
            project.AddFile("Class1.cs");
            m_solutionTestHelper.AddProject(Project2, project);

            projectDestination = Path.Combine(m_solutionDestination, Project3);
            project = new CSharpProject(projectDestination);
            project.AddFile("AssemblyInfo.cs");
            project.AddFile("Class1.cs");
            m_solutionTestHelper.AddProject(Project3, project);

            projectDestination = Path.Combine(m_solutionDestination, Project4);
            project = new CSharpProject(projectDestination);
            project.AddFile("AssemblyInfo.cs");
            project.AddFile("Class1.cs");
            m_solutionTestHelper.AddProject(Project4, project);

            m_initialVersions = new Hashtable();
        }

        [SetUp] 
        public void SetUp() {
            m_solutionTestHelper.CopySolution(m_solutionDestination);
        }

        [TearDown] 
        public void TearDown() {
            m_solutionTestHelper.DeleteDestination();
        }

        public void TestAllUnmodified() {
            // mark all projects unmodified
            m_solutionTestHelper.SetProjectModified(Project1, false);
            m_solutionTestHelper.SetProjectModified(Project2, false);
            m_solutionTestHelper.SetProjectModified(Project3, false);
            m_solutionTestHelper.SetProjectModified(Project4, false);
            // set configuration
            VcbConfiguration configuration = new VcbConfiguration();
            configuration.NumberingOptions.IncrementScheme = IncrementScheme.IncrementRevision;
            configuration.NumberingOptions.DefaultVersionType = AssemblyVersionType.AssemblyVersion;
            SourceSafeUserOptions sourceSafeOptions = new SourceSafeUserOptions();

            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                CollectInitialVersions(sfr);
                ApplyVersionChanges(sfr, ProjectFilter.PassAll);
            }
            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                ValidateVersionChanges(sfr, configuration, sourceSafeOptions);
            }
        }

        public void TestAllModified() {
            // mark all projects modified
            m_solutionTestHelper.SetProjectModified(Project1, true);
            m_solutionTestHelper.SetProjectModified(Project2, true);
            m_solutionTestHelper.SetProjectModified(Project3, true);
            m_solutionTestHelper.SetProjectModified(Project4, true);
            // set configuration
            VcbConfiguration configuration = new VcbConfiguration();
            configuration.NumberingOptions.IncrementScheme = IncrementScheme.IncrementRevision;
            configuration.NumberingOptions.DefaultVersionType = AssemblyVersionType.AssemblyVersion;
            SourceSafeUserOptions sourceSafeOptions = new SourceSafeUserOptions();

            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                CollectInitialVersions(sfr);
                ApplyVersionChanges(sfr, ProjectFilter.PassAll);
            }
            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                ValidateVersionChanges(sfr, configuration, sourceSafeOptions);
            }
        }

        public void TestSomeModified() {
            // mark some projects modified
            m_solutionTestHelper.SetProjectModified(Project1, true);
            m_solutionTestHelper.SetProjectModified(Project2, false);
            m_solutionTestHelper.SetProjectModified(Project3, true);
            m_solutionTestHelper.SetProjectModified(Project4, false);
            // set configuration
            VcbConfiguration configuration = new VcbConfiguration();
            configuration.NumberingOptions.IncrementScheme = IncrementScheme.IncrementRevision;
            configuration.NumberingOptions.DefaultVersionType = AssemblyVersionType.AssemblyVersion;
            SourceSafeUserOptions sourceSafeOptions = new SourceSafeUserOptions();

            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                CollectInitialVersions(sfr);
                ApplyVersionChanges(sfr, ProjectFilter.PassAll);
            }
            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                ValidateVersionChanges(sfr, configuration, sourceSafeOptions);
            }
        }

        /// <summary>
        ///   Tests modification of version type that does not exist
        /// </summary>
        public void TestAssemblyFileVersionModification() {
            // mark some projects modified
            m_solutionTestHelper.SetProjectModified(Project1, true);
            m_solutionTestHelper.SetProjectModified(Project2, false);
            m_solutionTestHelper.SetProjectModified(Project3, true);
            m_solutionTestHelper.SetProjectModified(Project4, false);
            // set configuration
            VcbConfiguration configuration = new VcbConfiguration();
            configuration.NumberingOptions.IncrementScheme = IncrementScheme.IncrementRevision;
            configuration.NumberingOptions.DefaultVersionType = AssemblyVersionType.AssemblyInformationalVersion;
            SourceSafeUserOptions sourceSafeOptions = new SourceSafeUserOptions();

            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                CollectInitialVersions(sfr);
                ApplyVersionChanges(sfr, ProjectFilter.PassAll);
            }
            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                ValidateVersionChanges(sfr, configuration, sourceSafeOptions);
            }
        }

        public void TestExcludeFilter() {
            // mark all projects modified
            m_solutionTestHelper.SetProjectModified(Project1, true);
            m_solutionTestHelper.SetProjectModified(Project2, true);
            m_solutionTestHelper.SetProjectModified(Project3, true);
            m_solutionTestHelper.SetProjectModified(Project4, true);
            // set configuration
            VcbConfiguration configuration = new VcbConfiguration();
            configuration.NumberingOptions.IncrementScheme = IncrementScheme.IncrementRevision;
            configuration.NumberingOptions.DefaultVersionType = AssemblyVersionType.AssemblyVersion;
            SourceSafeUserOptions sourceSafeOptions = new SourceSafeUserOptions();

            string[] projectsToExclude = new string[] { Path.GetFileNameWithoutExtension(Project2), Path.GetFileNameWithoutExtension(Project3) };
            IProjectFilter filter = new ProjectFilterByName(true, true, true, true, new string[0], projectsToExclude, new string[0]);

            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                CollectInitialVersions(sfr);
                ApplyVersionChanges(sfr, filter);
            }
            // validate that only modified projects not in the list of excluded has been updated
            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                foreach(ProjectInfo pi in sfr.ProjectInfoList) {
                    AssemblyVersions initialVersion = (AssemblyVersions)m_initialVersions[pi.ProjectName];
                    AssemblyVersions newVersion = pi.CurrentAssemblyVersions;
                    if (m_solutionTestHelper.IsProjectMarkedForModification(pi.ProjectName) && Array.IndexOf(projectsToExclude, pi.ProjectName) == -1) {
                        Assert.IsTrue(AssemblyVersionsHasIncremented(initialVersion, newVersion, configuration.NumberingOptions.DefaultVersionType));
                    }
                    else {
                        Assert.IsTrue(AssemblyVersionsAreEqual(initialVersion, newVersion, AssemblyVersionType.All));
                    }
                }
            }
        }

        public void TestIncludeFilter() {
            // mark all projects modified
            m_solutionTestHelper.SetProjectModified(Project1, true);
            m_solutionTestHelper.SetProjectModified(Project2, true);
            m_solutionTestHelper.SetProjectModified(Project3, true);
            m_solutionTestHelper.SetProjectModified(Project4, true);
            // set configuration
            VcbConfiguration configuration = new VcbConfiguration();
            configuration.NumberingOptions.IncrementScheme = IncrementScheme.IncrementRevision;
            configuration.NumberingOptions.DefaultVersionType = AssemblyVersionType.AssemblyVersion;
            SourceSafeUserOptions sourceSafeOptions = new SourceSafeUserOptions();

            string[] projectsToInclude = new string[] { Path.GetFileNameWithoutExtension(Project2), Path.GetFileNameWithoutExtension(Project3) };
            IProjectFilter filter = new ProjectFilterByName(true, true, true, true, projectsToInclude, new string[0], new string[0]);

            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                CollectInitialVersions(sfr);
                ApplyVersionChanges(sfr, filter);
            }
            // validate that only modified projects not in the list of excluded has been updated
            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                foreach(ProjectInfo pi in sfr.ProjectInfoList) {
                    AssemblyVersions initialVersion = (AssemblyVersions)m_initialVersions[pi.ProjectName];
                    AssemblyVersions newVersion = pi.CurrentAssemblyVersions;
                    if (m_solutionTestHelper.IsProjectMarkedForModification(pi.ProjectName) && Array.IndexOf(projectsToInclude, pi.ProjectName) != -1) {
                        Assert.IsTrue(AssemblyVersionsHasIncremented(initialVersion, newVersion, configuration.NumberingOptions.DefaultVersionType));
                    }
                    else {
                        Assert.IsTrue(AssemblyVersionsAreEqual(initialVersion, newVersion, AssemblyVersionType.All));
                    }
                }
            }
        }

        public void TestForceFilter1() {
            // mark some projects modified
            m_solutionTestHelper.SetProjectModified(Project1, true);
            m_solutionTestHelper.SetProjectModified(Project2, false);
            m_solutionTestHelper.SetProjectModified(Project3, false);
            m_solutionTestHelper.SetProjectModified(Project4, true);
            // set configuration
            VcbConfiguration configuration = new VcbConfiguration();
            configuration.NumberingOptions.IncrementScheme = IncrementScheme.IncrementRevision;
            configuration.NumberingOptions.DefaultVersionType = AssemblyVersionType.AssemblyVersion;
            SourceSafeUserOptions sourceSafeOptions = new SourceSafeUserOptions();
            // include not modified projects into list to force
            string[] projectsToForce = new string[] { Path.GetFileNameWithoutExtension(Project2), Path.GetFileNameWithoutExtension(Project3) };
            IProjectFilter filter = new ProjectFilterByName(true, true, true, true, new string[0], new string[0], projectsToForce);

            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                CollectInitialVersions(sfr);
                ApplyVersionChanges(sfr, filter);
            }
            // validate that all projects has been updated
            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                foreach(ProjectInfo pi in sfr.ProjectInfoList) {
                    AssemblyVersions initialVersion = (AssemblyVersions)m_initialVersions[pi.ProjectName];
                    AssemblyVersions newVersion = pi.CurrentAssemblyVersions;
                    if (m_solutionTestHelper.IsProjectMarkedForModification(pi.ProjectName) || Array.IndexOf(projectsToForce, pi.ProjectName) != -1) {
                        Assert.IsTrue(AssemblyVersionsHasIncremented(initialVersion, newVersion, configuration.NumberingOptions.DefaultVersionType));
                    }
                    else {
                        Assert.IsTrue(AssemblyVersionsAreEqual(initialVersion, newVersion, AssemblyVersionType.All));
                    }
                }
            }
        }

        public void TestForceFilter2() {
            // no projects modified
            m_solutionTestHelper.SetProjectModified(Project1, false);
            m_solutionTestHelper.SetProjectModified(Project2, false);
            m_solutionTestHelper.SetProjectModified(Project3, false);
            m_solutionTestHelper.SetProjectModified(Project4, false);
            // set configuration
            VcbConfiguration configuration = new VcbConfiguration();
            configuration.NumberingOptions.IncrementScheme = IncrementScheme.IncrementRevision;
            configuration.NumberingOptions.DefaultVersionType = AssemblyVersionType.AssemblyVersion;
            SourceSafeUserOptions sourceSafeOptions = new SourceSafeUserOptions();
            // include not modified projects into list to force
            string[] projectsToForce = new string[] { Path.GetFileNameWithoutExtension(Project2), Path.GetFileNameWithoutExtension(Project3) };
            IProjectFilter filter = new ProjectFilterByName(true, true, true, true, new string[0], new string[0], projectsToForce);

            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                CollectInitialVersions(sfr);
                ApplyVersionChanges(sfr, filter);
            }
            // validate that all projects has been updated
            using (SolutionFileReader sfr = SlnFileReader.SlnFileReaderFactory.GetSlnFileReader(SolutionFilename, configuration, sourceSafeOptions)) {
                foreach(ProjectInfo pi in sfr.ProjectInfoList) {
                    AssemblyVersions initialVersion = (AssemblyVersions)m_initialVersions[pi.ProjectName];
                    AssemblyVersions newVersion = pi.CurrentAssemblyVersions;
                    if (m_solutionTestHelper.IsProjectMarkedForModification(pi.ProjectName) || Array.IndexOf(projectsToForce, pi.ProjectName) != -1) {
                        Assert.IsTrue(AssemblyVersionsHasIncremented(initialVersion, newVersion, configuration.NumberingOptions.DefaultVersionType));
                    }
                    else {
                        Assert.IsTrue(AssemblyVersionsAreEqual(initialVersion, newVersion, AssemblyVersionType.All));
                    }
                }
            }
        }





        private void CollectInitialVersions(SolutionBrowser solutionBrowser) {
            m_initialVersions.Clear();
            // store current versions and apply new version
            foreach(ProjectInfo pi in solutionBrowser.ProjectInfoList) {
                Assert.IsTrue(pi.Modified == m_solutionTestHelper.IsProjectMarkedForModification(pi.ProjectName));
                m_initialVersions.Add(pi.ProjectName, pi.CurrentAssemblyVersions.Clone());
            }
        }

        private void ApplyVersionChanges(SolutionBrowser solutionBrowser, IProjectFilter filter) {
            solutionBrowser.ApplyFilter(filter);
            solutionBrowser.SaveVersions();
        }

        private void ValidateVersionChanges(SolutionBrowser solutionBrowser, VcbConfiguration configuration, SourceSafeUserOptions sourceSafeOptions) {
            // check that projects haven't been "modified"
            foreach(ProjectInfo pi in solutionBrowser.ProjectInfoList) {
                AssemblyVersions initialVersion = (AssemblyVersions)m_initialVersions[pi.ProjectName];
                AssemblyVersions newVersion = pi.CurrentAssemblyVersions;
                if (m_solutionTestHelper.IsProjectMarkedForModification(pi.ProjectName)) {
                    Assert.IsTrue(AssemblyVersionsHasIncremented(initialVersion, newVersion, configuration.NumberingOptions.DefaultVersionType));
                }
                else {
                    Assert.IsTrue(AssemblyVersionsAreEqual(initialVersion, newVersion, AssemblyVersionType.All));
                }
            }
        }

        private bool AssemblyVersionsHasIncremented(AssemblyVersions initial, AssemblyVersions incremented, AssemblyVersionType versionTypesToIncrement) {
            foreach (AssemblyVersionType avt in AssemblyVersions.AssemblyVersionTypes) {
                Debug.Assert((initial[avt] == ProjectVersion.Empty) || (initial[avt] <= incremented[avt]));
                // if version is in update filter
                if ((versionTypesToIncrement & avt) == avt) {
                    // check only if there was saome version initially
                    if (initial[avt] != ProjectVersion.Empty) {
                        if (initial[avt] == incremented[avt]) {
                            return false;
                        }
                    }
                }
                else if (initial[avt] != incremented[avt]) {
                    return false;
                }
            }
            return true;
        }

        private bool AssemblyVersionsAreEqual(AssemblyVersions initial, AssemblyVersions incremented, AssemblyVersionType versionTypesToCompare) {
            foreach (AssemblyVersionType avt in AssemblyVersions.AssemblyVersionTypes) {
                if ((versionTypesToCompare & avt) == avt) {
                    if (initial[avt] != incremented[avt]) {
                        return false;
                    }
                }
            }
            return true;
        }

        private string GetProjectName(string projectFilename) {
            return Path.GetFileNameWithoutExtension(projectFilename);
        }

        private SolutionTestHelper m_solutionTestHelper;
        private string m_solutionSource;
        private string m_solutionDestination;
        private readonly string SolutionFilename;

        Hashtable m_initialVersions;

        private const string Project1 = "ConsoleApplication1\\ConsoleApplication1.csproj";
        private const string Project2 = "ConsoleApplication2\\ConsoleApplication2.csproj";
        private const string Project3 = "ConsoleApplication3\\ConsoleApplication3.csproj";
        private const string Project4 = "ConsoleApplication4\\ConsoleApplication4.csproj";

	}
}
